module fn.adt.graph.viz.GraphSharp


open System
open System.Windows

open QuickGraph
open QuickGraph.Algorithms.Search
open GraphSharp.Algorithms.Layout
open GraphSharp.Algorithms.Layout.Simple.Tree
open GraphSharp.Algorithms.Layout.Simple.Hierarchical

open fn.adt.graph.Digraph



type GraphLayout<'V,'E when 'V : not struct and 'E :> IEdge<'V>> () =
    let DEFAULT_SIZE = Size(30.0, 30.0)
    let LABEL_SHIFT = Point(-10.0, 5.0)

    let trim n (mn,x,y,w,h) =
        let len = Math.Sqrt( w*w + h*h )
        let r0 = n / len
        let r1 = (len - 2.0*n) / len
        (mn,x+w*r0,y+h*r0,w*r1,h*r1)


    member this.with_default_size (g : BidirectionalGraph<_,_>) =
        let sizes = dict [for v in g.Vertices -> v, DEFAULT_SIZE]
        //let sl = SimpleTreeLayoutAlgorithm(g, dict [], sizes, SimpleTreeLayoutParameters())
        //let sl = BalloonTreeLayoutAlgorithm(g, dict [], sizes, BalloonTreeLayoutParameters(MinRadius=20), 1 :> obj)
        let sl = EfficientSugiyamaLayoutAlgorithm(g, EfficientSugiyamaLayoutParameters(), sizes)
        sl.Compute()
        sl

    member this.drawing_primitives (g : BidirectionalGraph<'V,'E>) (sl : ILayoutAlgorithm<_,_,_>) =
        let sz = DEFAULT_SIZE
        let shft = LABEL_SHIFT
        let dx, dy = -sz.Width/2.0, sz.Height/2.0
        let vertices = [for KeyValue(v,p) in sl.VertexPositions -> ("ellipse", p.X+dx, -(p.Y+dy), sz.Width, sz.Height)]
        let vertex_labels = [for KeyValue(v,p) in sl.VertexPositions ->
                             (sprintf "text:%s" (v.ToString()), p.X+shft.X, -(p.Y+shft.Y), 0.0, 0.0)]
        let edges = [for e in g.Edges ->
                        let sp = sl.VertexPositions.[e.Source]
                        let dp = sl.VertexPositions.[e.Target]
                        trim (sz.Width/2.0) ("line", sp.X, -sp.Y, dp.X-sp.X, sp.Y-dp.Y)]
        let labeled_edges = [for e in g.Edges do
                              match e :> IEdge<_> with
                              | :? ITagged<'V> as x -> yield e,x
                              | _ -> ()]
        let edge_labels = [for e,x in labeled_edges ->
                            let sp = sl.VertexPositions.[e.Source]
                            let dp = sl.VertexPositions.[e.Target]
                            let label = x.Tag.ToString()
                            (sprintf "text:%s" label, (sp.X+dp.X)/2.0, -(sp.Y+dp.Y)/2.0, 0.0, 0.0)]

        List.concat [|vertices; vertex_labels; edges; edge_labels|]
